

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;

public class IndexFile2 extends File{
	public TableRecord tableRecord;
	public int sizeOfAnIndexRecord;
	public int totalIndexRecord;
	public int sizeOfTheKeyValue;
	public IndexFile2(TableRecord record) throws IOException{
		this.tableRecord = record;
		this.sizeOfAnIndexRecord = record.getSizeOfAnIndexRecord();
		this.totalIndexRecord = record.getNumberOfTotalIndexRecord();
		this.sizeOfTheKeyValue = record.getFieldSizes().get(0);
		this.raf = new RandomAccessFile(tableRecord.getTableName()+".in", "rw");
		if(size()==0){
			createAPage();
		}
	}
	public byte[] maxStirng(){
		byte[] max = new byte[sizeOfTheKeyValue];
		for(int i=0; i<sizeOfTheKeyValue; i++){
			max[i] = (byte) 127;
		}
		return max;
	}
	public void createAPage() throws IOException{
		int offset = numberOfPages()*512;
		raf.seek(offset);
		raf.write(maxStirng());
		raf.write(tableRecord.getNumberOfTotalIndexRecord());
	}
	public int findProperPageHelper(String s) throws IOException{
		String pageHeader;
		int offset =0;
		int pageNumber = numberOfPages();
		byte[] bytes = new byte[sizeOfTheKeyValue];
		for(int i=0;i<pageNumber;i++){
			offset = 512*i;
			raf.seek(offset);
			raf.read(bytes);
			pageHeader = byteToString(bytes);
			if(s.compareTo(pageHeader)<0){
				return i;
			}
		}
		return -1;
	}
	public int findTheProperPageToWrite(String s) throws IOException{
		String pageHeader;
		int offset =0;
		int pageNumber = numberOfPages();
		for(int i=0;i<pageNumber;i++){
			byte[] bytes = new byte[sizeOfTheKeyValue];
			offset = 512*i;
			raf.seek(offset);
			raf.read(bytes);
			pageHeader = byteToString(bytes);
			if(s.compareTo(pageHeader)<0){
				return i;
			}
		}
		offset += 512+sizeOfTheKeyValue;
		raf.seek(offset);
		int numOfAvailableRecords = raf.read();
		if(numOfAvailableRecords>0){
			return pageNumber-1;
		}else{
			createAPage();
			return pageNumber;
		}
	}
//	public ArrayList<Integer> isInTable(String s) throws IOException{
//	ArrayList<Integer> result = new ArrayList<Integer>();
//	int page = findProperPageHelper(s);
//	if(page>=0){
//		int offset = page*512+sizeOfTheKeyValue;
//		int numOfAvailableRecords = raf.read();
//		for(int i=0; i<tableRecord.getNumberOfTotalIndexRecord()-numOfAvailableRecords;i++){
//			byte[] b = new byte[sizeOfTheKeyValue];
//			offset += i*sizeOfAnIndexRecord;
//			raf.seek(offset);
//			raf.read(b);
//			String keyValue = byteToString(b);
//			if(s.equals(keyValue)){
//				result.add(page);
//				result.add(i);
//				return result;
//			}
//		}
//	}
//	result.add(-1);
//	return result;
//}
public void putTheRecordInTheRightPlace(int offset, int page) throws IOException{
	byte[] bytes1 = new byte[sizeOfAnIndexRecord];
	byte[] bytes2 = new byte[sizeOfAnIndexRecord];
	raf.seek(offset);
	raf.read(bytes1);
	String theKeyValue = byteToString(bytes1);
	if(offset-sizeOfAnIndexRecord>page*512){
		raf.seek(offset-sizeOfAnIndexRecord);
		raf.read(bytes2);
		String theComparedKeyValue = byteToString(bytes2);
	
//	raf.seek(offset-sizeOfAnIndexRecord);
//	raf.read(bytes2);
//	String theComparedKeyValue = byteToString(bytes2);
	if(theKeyValue.compareTo(theComparedKeyValue)<0){
		raf.seek(offset-sizeOfAnIndexRecord);
		raf.write(bytes1);
		raf.seek(offset);
		raf.write(bytes2);
		putTheRecordInTheRightPlace(offset-sizeOfAnIndexRecord,page);
	}
	}
}
public void addIndexRecord(String keyValue, ArrayList<Integer> placeOfDataRecord) throws IOException{
	byte[] theIndexRecord = createAnIndexRecord(keyValue, placeOfDataRecord);
	int pageToWrite = findTheProperPageToWrite(keyValue);
	int offset = pageToWrite*512+sizeOfTheKeyValue;
	int numOfAvaialbleRecords = raf.read();
	if(numOfAvaialbleRecords>0){
		offset += (tableRecord.getNumberOfTotalIndexRecord()-numOfAvaialbleRecords)*sizeOfAnIndexRecord+1;
		raf.seek(offset);
		raf.write(theIndexRecord);
		putTheRecordInTheRightPlace(offset,pageToWrite);
		updatePageHeader(pageToWrite);
	}
	else{
		byte[] temp = new byte[sizeOfAnIndexRecord];
		offset += tableRecord.getNumberOfTotalIndexRecord()*sizeOfAnIndexRecord;
		raf.seek(offset);
		raf.read(temp);
		raf.write(theIndexRecord);
		putTheRecordInTheRightPlace(offset,pageToWrite);
		updatePageHeader(pageToWrite);
		pushOneDown(temp, pageToWrite++);
	}
	
}
public void updatePageHeader(int pageNumber) throws IOException{
	int offset = pageNumber*512;
	int upperLimit = offset + 512;
	int lengthOfFile = (int) size();
	int limit = Math.min(upperLimit, lengthOfFile);
	int usedRecordNumber = (limit-offset-1)/sizeOfTheKeyValue;
	int theOffsetOfMaxKeyValue = offset+sizeOfTheKeyValue+1+(sizeOfTheKeyValue*(usedRecordNumber-1));
	byte[] theMaxKeyValue = new byte[sizeOfTheKeyValue];
	raf.seek(theOffsetOfMaxKeyValue);
	raf.read(theMaxKeyValue);
	raf.seek(offset);
	raf.write(theMaxKeyValue);
	raf.write(tableRecord.getNumberOfTotalIndexRecord()-usedRecordNumber);
}
public void pushOneDown(byte[] fromPreviousPage, int pageNumber) throws IOException{
	int offset = pageNumber*512+sizeOfTheKeyValue;
	raf.seek(offset);
	int availableRecords = raf.read();
	if(availableRecords>0){
		byte[] bytes = new byte[(tableRecord.getNumberOfTotalIndexRecord()-availableRecords)*sizeOfAnIndexRecord];
		raf.read(bytes);
		raf.seek(pageNumber*512+1);
		raf.write(fromPreviousPage);
		raf.write(bytes);
		updatePageHeader(pageNumber);
	}else{
		byte[] bytes = new byte[(tableRecord.getNumberOfTotalIndexRecord()-1)*sizeOfAnIndexRecord];
		raf.read(bytes);
		byte[] toNextPage = new byte[sizeOfAnIndexRecord];
		raf.read(toNextPage);
		raf.seek(offset+1);
		raf.write(fromPreviousPage);
		raf.write(bytes);
		updatePageHeader(pageNumber);
		pushOneDown(toNextPage, pageNumber++);
	}
}
public byte[] createAnIndexRecord(String keyValue, ArrayList<Integer> placeOfDataRecord){
	byte[] bytes = new byte[sizeOfAnIndexRecord];
	byte[] keyValueInByte = stringToByte(keyValue);
	System.arraycopy(keyValueInByte, 0, bytes, 0, keyValueInByte.length);
	bytes[sizeOfAnIndexRecord-2] = placeOfDataRecord.get(0).byteValue();
	bytes[sizeOfAnIndexRecord-1] = placeOfDataRecord.get(1).byteValue();
	return bytes;
}
@Override
public Record byteToRecord(byte[] bytes) {
	// TODO Auto-generated method stub
	return null;
}
}
